<?
package('jpunit.framework');

import('jpunit.framework.AssertionFailedError');
import('japha.lang.StringBuffer');

/**
 * $Id: Assert.php,v 1.10 2004/08/26 00:51:28 japha Exp $
 *
 * A set of assert methods.  Messages are only displayed when an assert fails.
 *
 * This class need to find a way to implement all of the methods that expect Object values
 * as their parameters but yet also need to be able to take a null value (since in Java null
 * is still technically an Object). Need to do this with the minimum possible amount of
 * extra fat.
 *
 * @author <a href="mailto:gantt@cs.montana.edu">Ryan Gantt</a>
 * @version $Revision: 1.10 $
 */
class Assert extends Object
{
	/**
	 * Protect constructor since it is a static only class
	 */
	public function __construct(){}

	/**
	 * Asserts that a condition is true. If it isn't, it calls Assert::fail which
	 * throws an AssertionFailedError with the given message.
	 *
	 * One args: boolean
	 * Two args: string, boolean
	 *
	 * @see Overloaded methods
	 */
	public static function assertTrue()
	{
		$argv = func_get_args();
		switch( func_num_args() )
		{
			case 1:
			{
				return self::assertTrue0( $argv[0] );
			}
			case 2:
			{
				return self::assertTrue1( $argv[0], $argv[1] );
			}
		}
	}

	/**
	 * assertTrue( boolean )
	 *
	 * This method delegates to assertTrue( string, boolean ) by sending a null value as the message
	 *
	 * @overloaded assertTrue
	 * @param condition The condition to check for a false value.
	 */
	public static function assertTrue0( $condition )
	{
		self::assertTrue( null, $condition );
	}

	/**
	 * assertTrue( string, boolean )
	 *
	 * @overloaded assertTrue
	 * @param message The message to add to the AssertionFailedError if the condition is false
	 * @param condition The boolean condition to check for a false value
	 */
	public static function assertTrue1( $message, $condition )
	{
		if( !$condition )
		{
			self::fail( $message );
		}
	}

	/**
	 * Asserts that a condition is false. If it isn't it throws
	 * an AssertionFailedError with the given message.
	 *
	 * One args: boolean
	 * Two args: string, boolean
	 *
	 * @see Overloaded methods
	 */
	public static function assertFalse()
	{
		$argv = func_get_args();
		switch( func_num_args() )
		{
			case 1:
			{
				return self::assertFalse0( $argv[0] );
			}
			case 2:
			{
				return self::assertFalse1( $argv[0], $argv[1] );
			}
		}
	}

	/**
	 * assertFalse( boolean )
	 *
	 * Simply delegates to assertFalse( string, boolean ) with a null message string
	 *
	 * @overloaded assertFalse
	 * @param condition The boolean condition to check for falseness
	 */
	public static function assertFalse0( $condition )
	{
		Assert::assertFalse( null, $condition );
	}

	/**
	 * assertFalse( string, boolean )
	 *
	 * @overloaded assertFalse
	 * @param message The message that will be passed to AssertionFailedError as well as the one put on top of the trace
	 * @param condition The boolean condition to check for falseness
	 */
	public static function assertFalse1( $message, $condition )
	{
		Assert::assertTrue( $message, !$condition );
	}

	/**
	 * Fails a test with the given message.
	 *
	 * @param message The message passed to the AssertionFailedError. This is the message that is displayed on top of the trace
	 * @throws AssertionFailedError Whenever this method is called
	 */
	public static function fail( $message )
	{
		throw new AssertionFailedError( $message );
	}

	/**
	 * Asserts that two objects are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * Two args: Object, Object
	 * Three args:
	 *       Arg1 is Object? string, Object, Object
	 *       Arg2 nt Object? string, primitive, primitive
	 *
	 * @see Overloaded methods
	 */
	public static function assertEquals()
	{
		$argv = func_get_args();
		switch( func_num_args() )
		{
			case 2:
			{
				return self::assertEquals0( $argv[0], $argv[1] );
			}
			case 3:
			{
				if( $argv[1] instanceof Object )
				{
					return self::assertEquals1( $argv[0], $argv[1], $argv[2] );
				}	
				return self::assertEquals2( $argv[0], $argv[1], $argv[2] );
			}
		}
	}

	/**
	 * assertEquals( Object, Object );
	 *
	 * This method delegates to assertEquals( string, Object, Object ) using the default message 'Condition'
	 *
	 * @overloaded assertEquals
	 * @param first The first object that will be compared
	 * @param second The object that is compared for equality against the first
	 * @throws AssertionFailedError If the delegated method does
	 */
	public static function assertEquals0( $first, $second )
	{
		self::assertEquals( 'Condition', $first, $second );
	}

	/**
	 * assertEquals( String, Object, Object );
	 *
	 * Runs the equals( Object ) method of the first object against the second object
	 *
	 * @overloaded assertEquals
	 * @param message The message to put in the AssertionFailedError, and in turn the message that will appear on top of the trace
	 * @param first The first object that will be compared
	 * @param second The object that is to be compared for equality against the first
	 * @throws AssertionFailedError If the the two objects are not equal in value
	 */
	public static function assertEquals1( $message, Object $first, $second )
	{
		if( !$first->equals( $second ) )
		{
			Assert::failNotEquals( $message, $first, $second );
		}
	}

	/**
	 * assertEquals( String, primitive, primitive );
	 *
	 * Performs the same operation as assertEquals( string, Object, Object ) except that the procedure is performed
	 * on primitive data types instead of instances of user-defined types
	 *
	 * @overloaded assertEquals
	 * @param message The message to put in the AssertioNFailedError, and in turn the message that will appear on top of the trace
	 * @param first The first primitive that will be compared
	 * @param second The second primitive that will be compared
	 * @throws AssertionFailedError If the two primitives do not have the same value
	 */
	public static function assertEquals2( $message, $first, $second )
	{
		if( ! ( $first == $second ) )
		{
			Assert::failNotEquals( $message, $first, $second );
		}
	}

	/**
	 * Asserts that an object isn't null. If it is
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @see Overloaded methods
	 */
	public static function assertNotNull()
	{
		$argv = func_get_args();
		switch( func_num_args() )
		{
			case 1:
			{
				return self::assertNotNull0( $argv[0] );
			}
			case 2:
			{
				return self::assertNotNull1( $argv[0], $argv[1] );
			}
		}
	}

	/**
	 * assertNotNull( Object )
	 *
	 * Simply delegates execution to assertTrue( boolean )
	 *
	 * @overloaded assertNotNull
	 * @param object The object that may or may not be null
	 */
	public static function assertNotNull0( $object )
	{
		return self::assertTrue( $object != null );
	}

	/**
	 * assertNotNull( string, Object )
	 *
	 * Simply delegates execution to assertTrue( string, boolean )
	 *
	 * @overloaded assertNotNull
	 * @param message The message to pass to the AssertionFailedError, and in turn the message that will be shown at the top of the trace
	 */
	public static function assertNotnUll1( $message, $object )
	{
		return self::assertTrue( $message, $object != null );
	}

	/**
	 * Asserts that an object is null.  If it is not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param object The object that may or may not be null
	 */
	public static function assertNull( $message, $object )
	{
		Assert::assertTrue( $message, $object == null );
	}

	/**
	 * Asserts that two objects refer to the same object. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param expected The expected value of the "actual" parameter
	 * @param actual The actual value that will be compared to the first
	 */
	public static function assertSame( $message, Object $expected, Object $actual )
	{
		if ( ! ( $expected == $actual ) )
		{
			Assert::failNotSame( $message, $expected, $actual );
		}
	}

 	/**
	 * Asserts that two objects refer to the same object. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param expected A value that the "actual" parameter should NOT be
	 * @param actual The acual value that will be compared to the first
	 */
	public static function assertNotSame( $message, Object $expected, Object $actual )
	{
		if ( $expected == $actual )
		{
			Assert::failSame( $message );
		}
	}

	/**
	 * Formats an error based on a the message that is passed
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The string message to be formatted
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	public static function format( $message, $expected, $actual )
	{
		$formatted = new StringBuffer('');
		if ( $message != null )
		{
			$formatted->append( $message . ' ' );
		}
		$formatted->append('expected: &lt;'.$expected.'&gt; but was:&lt;'.( $actual != null ? $actual : 'null' ).'&gt;');
		return $formatted->toString();
	}

	/**
	 * Called when an assertion has failed, and the two objects are a reference to the same memory location
	 *
	 * @param message The message that will be formatted, data appended to, and pushed to the top of the stack trace
	 * @throws AssertionFailedError If the called method does
	 */
	private static function failSame( $message )
	{
		$formatted = new StringBuffer('');
 		if ( $message != null )
 		{
 			$formatted->append( $message.' ' );
 		}
 		$formatted->append('expected not same');
 		Assert::fail( $formatted->toString() );
	}

	/**
	 * Called when an assertion has failed, and the two objects are not a reference to the same memory location
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The message that will be formatted, data appended to, and pushed to the top of the stack trace
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	private static function failNotSame( $message, $expected, $actual )
	{
		$formatted = new StringBuffer('');
		if ( $message != null )
		{
			$formatted->append( $message.' ' );
		}
		$formatted->append('expected same: <'.$expected.'> was not: <'.$actual.'>');
		Assert::fail( $formatted->toString() );
	}

	/**
	 * Called when an assertion has failed, and the two objects are not equal in value
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The message that will be formatted and then immediately pushed to the top of the stack trace
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	private static function failNotEquals( $message, $expected, $actual)
	{
		Assert::fail( Assert::format( $message, $expected, $actual ) );
	}
	
	public static function assertException( $eblock )
	{
		try
		{
			eval( $eblock );
		}
		catch( Exception $e )
		{
			return true;
		}
		return false;
	}
}
?>
